package asteroids.model.programs.statements.ProgramStatements;

import java.util.Map;

import asteroids.model.programs.expressions.Expression;
import asteroids.model.programs.statements.ProgramStatement;
import asteroids.model.programs.types.Type;

/**
 * A class representing an AssignStatement, this class is a sub-class of the
 * Statement-class.
 * 
 * An AssignStatement will create a new variable and assign a certain expression
 * to it.
 * 
 * @author Michiel De Cuyper
 * @version 13.05
 * 
 */
public class AssignStatement extends ProgramStatement {

	/**
	 * Constructor for the AssignStatement class
	 * 
	 * @param line
	 *            The line on which this if-statement is found
	 *            Gets passed to the Statement-class
	 * @param column
	 *            The column in which this if-statement is found
	 *            Gets passed to the Statement-class
	 * @param name
	 *            The name of the variable
	 * @param rhs
	 *            An expression to be assigned to the variable
	 */
	public AssignStatement(int line, int column, String name, Expression rhs) {
		super(line, column);
		this.setName(name);
		this.setRhs(rhs);
	}

	/**
	 * Sets the name of this variable to a given name
	 * 
	 * @param name
	 *            The name to be set
	 * 
	 * @post ...
	 *       | (new this).getName() == name
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * Returns the name of this variable
	 * 
	 * @return ...
	 *         | return this.name
	 */
	public String getName() {
		return this.name;
	}

	/**
	 * A String storing the name of the variable that is assigned
	 */
	private String name;

	/**
	 * Sets the right hand side of this assignement to a given right hand side
	 * 
	 * @param rhs
	 *            The right hand side to be set
	 * 
	 * @post ...
	 *       | (new this).getRhs == rhs
	 * 
	 * @pre ...
	 *      | rhs instanceof BasicExpression
	 */
	public void setRhs(Expression rhs) {
		this.rhs = rhs;
	}

	/**
	 * Returns the right hand side of this assignement
	 * 
	 * @return ...
	 *         | return this.rhs
	 */
	public Expression getRhs() {
		return this.rhs;
	}

	/**
	 * An expression storing the right hand side of this assignement.
	 */
	private Expression rhs;

	/**
	 * Executes this assignement, by creating a new variable and assigning it's
	 * name and value to it
	 * 
	 * @effect ...
	 *         | new var(name, line column)
	 *         | var.assign(this.getRhs().getResult())
	 */
	@Override
	public void execute() {
		getProgram().setVariableReference(name, rhs.getResult());
	}

	@Override
	public Expression getExpression() {
		return rhs;	
	}
	
	@Override
	public String typeCheckStatement(String errorMessage){
		String newErrorMessage = getExpression().typeCheckExpression(errorMessage);
		Map<String, Type> globals = getProgram().getGlobals();
		
		if(!globals.containsKey(getName())){ //The variable is not within the global variable map
			if(getNextStart() != null){ //Means that is in the main statement, and thus should be a global variable.
				String addErrorMessage = ("The variable mentioned at line " + getLine() + 
										" and column " + getColumn() + " is not a global variable\n");
				if(newErrorMessage == null) newErrorMessage = addErrorMessage;
				else newErrorMessage += addErrorMessage;
				
			}		
		}
		else if(getExpression().getType() != globals.get(getName()) && getExpression().getType() != Type.ANY){//Type.ANY happens when the rhs is null
			String addErrorMessage = ("The variable assigned at line " + getLine() + 
									" and column " + getColumn() + " is of type " + getExpression().getType().toString() 
									+ ", but should be of type " + globals.get(getName()).toString() + "\n");
			if(newErrorMessage == null) newErrorMessage = addErrorMessage;
			else newErrorMessage += addErrorMessage;
		}	
		return newErrorMessage;
	}
}
